#include "stdafx.h"
#include "../Object.h"
#include "../String.h"
#include "../Value.h"
#include "../Function.h"

namespace Gs
{
	Value nilValue;

	// Initialises a new instance of the Object class.
	Object::Object()
		: _array(0)
	{
		for(int i = 0; i < ObjectHashBuckets; i++)
		{
			_buckets[i] = 0;
			_bucketSizes[i] = _bucketLengths[i] = 0;
		}
	}

	// Destroys the instance.
	Object::~Object()
	{
		for(int i = 0; i < ObjectHashBuckets; i++)
		{
			delete _buckets[i];
		}
	}

	// Assigns a value to a key in the object.
	void Object::SetValue(String *key, const Value &value)
	{
		if (!key || !((String *)key)->IsIdentifier())
			throw InvalidParameterException("name", "A valid, non-zero length identifier");

		unsigned int bucketIndex = key->GetHash() % ObjectHashBuckets;
		BucketKeyValuePair *&bucket = _buckets[bucketIndex];
		if (!bucket)
		{
			// create new bucket and assign.
			bucket = new BucketKeyValuePair[MinimumBucketSize];
			_bucketLengths[bucketIndex] = 1;
			_bucketSizes[bucketIndex] = MinimumBucketSize;
			bucket[0].key = key;
			bucket[0].value = value;
		}
		else
		{
			// search through existing keys/value pairs.
			unsigned short &length = _bucketLengths[bucketIndex];
			for(int i = 0; i < length; i++)
			{
				if (bucket[i].key->IsEqual(key))
				{
					bucket[i].value = value;
					return;
				}
			}
			
			// grow bucket if needed
			unsigned short &size = _bucketSizes[bucketIndex];
			if (length == size)
			{
				int newSize = size * 2;
				BucketKeyValuePair *newBucket = new BucketKeyValuePair[newSize];
				for(int i = 0; i < size; i++)
					newBucket[i] = bucket[i];
				delete [] bucket;
				bucket = newBucket;
				size = newSize;
			}
			
			// assign new key/value pair
			bucket[length].key = key;
			bucket[length].value = value;
			length++;
		}
	}

	// Gets a value from a key in the object.
	Value *Object::GetValue(String *key) const
	{
		unsigned int bucketIndex = key->GetHash() % ObjectHashBuckets;
		const BucketKeyValuePair *bucket = _buckets[bucketIndex];
		if (!bucket)
			return 0;
		
		int length = _bucketLengths[bucketIndex];
		for(int i = 0; i < length; i++)
		{
			if (bucket[i].key->IsEqual(key))
			{
				return (Value *)&bucket[i].value;
			}
		}
		return 0;
	}

	// Removes the specified field (resolving to nil).
	void Object::RemoveField(IString *name)
	{
		Value *val = GetValue((String *)name);
		if (val)
			val->Set();
	}

	// Sets the specified field to an integer value.
	void Object::SetField(IString *name, int i)
	{
		Value val;
		val.Set(i);
		SetValue((String *)name, val);
	}

	// Sets the specified field to a float value.
	void Object::SetField(IString *name, float f)
	{
		Value val;
		val.Set(f);
		SetValue((String *)name, val);
	}

	// Sets the specified field to a string value.
	void Object::SetField(IString *name, IContext *context, const char *s)
	{
		if (!context)
			throw InvalidParameterException("context", "A valid IContext pointer.");
		Value val;
		val.Set();
		SetValue((String *)name, val);
	}

	// Sets the specified field to a string value.
	void Object::SetField(IString *name, IString *s)
	{
		Value val;
		val.Set(s);
		SetValue((String *)name, val);
	}

	// Sets the specified field to an object.
	void Object::SetField(IString *name, IObject *o)
	{
		Value val;
		val.Set(o);
		SetValue((String *)name, val);
	}

	// Sets the specified field to a native function.
	void Object::SetField(IString *name, NativeFunction f)
	{
		if (!f)
			throw InvalidParameterException("f", "A valid native function pointer.");
		Function *func = new Function(f);
		Value val;
		val.Set(func);
		SetValue((String *)name, val);
	}

	// Sets the specified field to a user pointer.
	void Object::SetField(IString *name, UserPointer ptr)
	{
		Value val;
		val.Set(ptr);
		SetValue((String *)name, val);
	}

	// Gets the value at the named key.
	IValue *Object::GetField(IString *name) const
	{
		Value *val = GetValue((String *)name);
		if (!val)
			return &nilValue;
		return val;
	}
}